package com.example.prison;

import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.example.prison.entity.*;
import com.example.prison.mapper.CarTrafficRecordMapper;
import com.example.prison.mapper.DeviceManagementMapper;
import com.example.prison.service.AttendanceDeviceInfoService;
import com.example.prison.service.CarManagementService;
import com.example.prison.service.UseCarManagementService;
import com.example.prison.util.TCPClient;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Objects;

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
public class PrisonApplication {

    private static Logger logger = LoggerFactory.getLogger(PrisonApplication.class);
    public static final String getsnCmd = "{\"cmd\" :\"getsn\"}";
    public static final int ENABLE_PUSH = 1;
    public static final int JSON_FMT = 1;
    public static final int ENABLE_IMAGE = 1;
    public static final int BLOCK_TYPE_BIN_RESULT = 1;
    public static final int BLOCK_TYPE_IMAGE_DATA = 2;
    public static String numberplate;
    private static CarTrafficRecordMapper carTrafficRecordMapper;
    private static DeviceManagementMapper deviceManagementMapper;
    private static AttendanceDeviceInfoService attendanceDeviceInfoService;
    private static UseCarManagementService useCarManagementService;
    private static CarManagementService carManagementService;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(PrisonApplication.class, args);

//        TCPClient tcpClient = new TCPClient();
//        tcpClient.run("192.168.3.140", 8886);

        //        Date date = new Date();
//        int hours = date.getHours();
//        while (hours < 24) {
////            carRecord("10.10.10.3");//出
//            carRecord("10.10.10.2");//进
//        }

        System.out.println("启动成功！");
    }



    @SneakyThrows
    public static void carRecord(String ip) {
        //Socket socket = new Socket("10.10.10.3", 8131);
        Socket socket = new Socket(ip, 8131);

        //配置的方式：主动推送，JSON格式，带图片
        configFormat(socket, ENABLE_PUSH, JSON_FMT, ENABLE_IMAGE);

        AddWhiteList(socket);
        //QueryWhiteList(socket);

        // sendKeepAlive(socket);
        int count = 0;
        boolean run = true;
        //socket.setSoTimeout(2*1000);
        socket.setSoTimeout(1000);
        while (run) {
            // 5秒发一次心跳包
            if (count > 5) {
                sendKeepAlive(socket);
                count = 0;
            }
            count++;
            int packetLen = recvPacketSize(socket);
            if (packetLen > 0) {
                //接收实际数据
                byte[] data = new byte[packetLen];
                int recvLen = recvBlock(socket, data, packetLen);//接收指定长度的数据
                if (recvLen > 1000) {
                    //System.out.println("接收实际数据data:" + data);
                    //System.out.println("接收指定长度的数据recvLen:" + recvLen);
                    onRecv(data, recvLen);

                    //查询白名单
                    QueryWhiteList(socket);
                    //System.out.println("发送查询白名单命令");
                    int queryPacketLen = recvPacketSize(socket);
                    if (queryPacketLen > 0) {
                        //接收实际数据
                        byte[] queryData = new byte[queryPacketLen];
                        int queryRecvLen = recvBlock(socket, queryData, queryPacketLen);//接收指定长度的数据
                        if (queryRecvLen > 0) {
                            //System.out.println("queryData:" + queryData);
                            //System.out.println("queryRecvLen:" + queryRecvLen);
                            onRecv(queryData, queryRecvLen);
                            System.out.println("发送开闸命令");
                            // 发送开闸命令
                            String triggerCmd1 = "{\"cmd\":\"ioctl\",\"io\" :0,\"value\":2,\"delay\":500}";
                            sendCmd(socket, triggerCmd1);
                        } else {
                            System.out.println("无车辆通过");
                        }
                    }

                } else {
                    //System.out.println("无车辆通过");
                    System.out.println("socket error!");
                }
            } else if (packetLen == 0) {
                //接收到心跳包
                System.out.println("recv a keep-alive packet!");
            } else {
                //error
                System.out.println("msg fmt wrong!");
            }
            Thread.sleep(500);
        }

        socket.close();
        logger.info("已开启");
    }

    @SneakyThrows
    public static void specialCarRecord(String ip) {

        Integer flag =  0;
        ArrayList<String> carLines = new ArrayList<>();
        String fileName = "carNumber.txt";
        // 带缓冲的流读取，默认缓冲区8k
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(fileName), StandardCharsets.UTF_8);
        BufferedReader reader = new BufferedReader(inputStreamReader);
        String line;
        while ((line = reader.readLine()) != null) {
            carLines.add(line);
        }
        //System.out.println(carLines);
        for(String car : carLines) {
            if(Objects.equals(car, numberplate)) {
                flag = 1;
            }
        }


        //Socket socket = new Socket("10.10.10.3", 8131);
        Socket socket = new Socket(ip, 8131);

        //配置的方式：主动推送，JSON格式，带图片
        configFormat(socket, ENABLE_PUSH, JSON_FMT, ENABLE_IMAGE);

        AddWhiteList(socket);
        //QueryWhiteList(socket);

        // sendKeepAlive(socket);
        int count = 0;
        boolean run = true;
        //socket.setSoTimeout(2*1000);
        socket.setSoTimeout(1000);
        while (run) {
            // 5秒发一次心跳包
            if (count > 3) {
                sendKeepAlive(socket);
                count = 0;
            }
            count++;
            int packetLen = recvPacketSize(socket);
            if (packetLen > 0) {
                //接收实际数据
                byte[] data = new byte[packetLen];
                int recvLen = recvBlock(socket, data, packetLen);//接收指定长度的数据
                if (recvLen > 1000) {
                    //System.out.println("接收实际数据data:" + data);
                    //System.out.println("接收指定长度的数据recvLen:" + recvLen);
                    onRecv(data, recvLen);

                    //查询白名单
                    QueryWhiteList(socket);
                    //System.out.println("发送查询白名单命令");
                    int queryPacketLen = recvPacketSize(socket);
                    if (queryPacketLen > 0) {
                        //接收实际数据
                        byte[] queryData = new byte[queryPacketLen];
                        int queryRecvLen = recvBlock(socket, queryData, queryPacketLen);//接收指定长度的数据
                        if (queryRecvLen > 0 || flag == 1) {
                            //System.out.println("queryData:" + queryData);
                            //System.out.println("queryRecvLen:" + queryRecvLen);
                            onRecv(queryData, queryRecvLen);
                            System.out.println("发送开闸命令");
                            // 发送开闸命令
                            String triggerCmd1 = "{\"cmd\":\"ioctl\",\"io\" :0,\"value\":2,\"delay\":500}";
                            sendCmd(socket, triggerCmd1);
                        } else {
                            System.out.println("无车辆通过");
                        }
                    }

                } else {
                    //System.out.println("无车辆通过");
                    System.out.println("socket error!");
                }
            } else if (packetLen == 0) {
                //接收到心跳包
                System.out.println("recv a keep-alive packet!");
            } else {
                //error
                System.out.println("msg fmt wrong!");
            }
            Thread.sleep(500);
        }

        socket.close();
        logger.info("已开启");
    }

    public static void QueryWhiteList(Socket socket) {
        String cmd = "{"
                + "\"cmd\":\"white_list_operator\","
                + "\"operator_type\":\"select\","
                + "\"plate\":" + numberplate + ","
//                + "\"plate\":\"川 A07273\","
                + "\"id\":\"999999\","
                + "\"sub_type\":\"plate\","
                + "} ";
        try {
            byte[] b = cmd.getBytes("gb2312");//编码
            String sa = new String(b, "gb2312");//解码

            System.out.println("白名单查询命令发送:" + cmd);
            sendCmd(socket, sa);
        } catch (Exception e) {
            System.out.println("Error:" + e);
            System.out.println("白名单对比失败");
        }

    }

    public static void AddWhiteList(Socket socket) {
        String cmd = "{"
                + "\"cmd\":\"white_list_operator\","
                + "\"operator_type\":\"update_or_add\","
                + "\"dldb_rec\":{"
                + "\"create_time\":\"2018-10-1 12:30:40\","
                + "\"enable_time\":\"2018-08-08 12:30:40\","
                + "\"overdue_time\":\"2018-12-12 12:30:40\","
                + "\"enable\":1,"
                + "\"plate\":\"川A12345\","
                + "\"time_seg_enable\":0,"
                + "\"seg_time\": \"\","
                + "\"need_alarm\":0,"
                + "\"vehicle_code\":\"123456\","
                + "\"vehicle_comment\":\"123456\","
                + "\"customer_id\":1"
                + "}"
                + "} ";
        try {
            byte[] b = cmd.getBytes("gb2312");//编码
            String sa = new String(b, "gb2312");//解码

            sendCmd(socket, sa);
        } catch (Exception e) {
            System.out.println("Error:" + e);
        }

    }

    public static void parseBinIVSResult(byte[] data, int len) {
        int pos = 0;

        if (data[2] == BLOCK_TYPE_BIN_RESULT) {
            int blockSize = convBytesToInt(data, 4);
            //data 8开始blockSize为结构体数据
            //图片数据
            pos = 8 + blockSize;
            if (data[pos] == 'I' && data[pos + 1] == 'R') {
                if (data[pos + 2] == BLOCK_TYPE_IMAGE_DATA) {
                    int imageSize = convBytesToInt(data, pos + 4);
                    //data pos+8开始imageSize具体的图片数据
                    //saveImage(data,pos,len);
                }
            }
        }
    }

    public static void onIVSResultRecv(byte[] data, int len) {
        //接收到识别结果的处理
        if (data[0] == 'I' && data[1] == 'R') {
            //二进制的结构体处理
            parseBinIVSResult(data, len);
        } else {
            //json处理
            int pos = 0;
            while (true) {
                if (data[pos] == 0) {
                    break;
                }
                pos++;
            }


            String ivs = new String(data, 0, pos);
            System.out.println(ivs);

            //
            ParsePlate(ivs);

            // 此处改为通过json来解析车牌识别结果,来获取车牌图片的大小
            int nImgSize = len - pos - 1;

            // 获取图片的大小
            saveImage(data, pos + 1, nImgSize, "D:\\test__1.jpg");
        }
    }

    protected static void WriteTxt(String path, String txt) {
        try {
            FileWriter f = new FileWriter(path);
            BufferedWriter bw = new BufferedWriter(f);
            bw.write(txt);
            bw.close();
        } catch (Exception e) {
        }
    }

    public static void ParsePlate(String jsonData) {
        try {
            JsonParser parser = new JsonParser();  //创建JSON解析器

            do {
                JsonObject jsonObject = (JsonObject) parser.parse(jsonData.toString());
                if (jsonObject == null || jsonObject.isJsonNull()) {
                    break;
                }

                // 解析PlateResult
                JsonObject jsonPlateResult = jsonObject.get("PlateResult").getAsJsonObject();
                if (jsonPlateResult == null || jsonPlateResult.isJsonNull()) {
                    break;
                }

                // 获取车牌号
                String license = jsonPlateResult.get("license").getAsString();
                if (license == null || license == "") {
                    break;
                }

                System.out.println("JSON解析器获取的车牌号:" + license);
                numberplate = license;
                WriteTxt("d:\\plate_license.txt", license);

                // 解析AlarmInfoPlate
                JsonObject jsonAlarmInfoPlate = jsonObject.get("AlarmInfoPlate").getAsJsonObject();
                if (jsonAlarmInfoPlate == null || jsonAlarmInfoPlate.isJsonNull()) {
                    break;
                }
                // 获取设备序列号
                String serialno = jsonAlarmInfoPlate.get("serialno").getAsString();
                if (serialno == null || serialno == "") {
                    break;
                }
                //车辆数据记录
                CarTrafficRecord record = new CarTrafficRecord();
                record.setNumber(license);
                record.setSn(serialno);
                record.setTime(new SimpleDateFormat("yyyy/M/d HH:mm:ss").format(new Date()));
                if(ObjectUtils.isNotEmpty(deviceManagementMapper.getOneByNumber(license))) {
                    DeviceManagement device = deviceManagementMapper.getOneByNumber(license);
                    record.setDescribe(device.getDeviceArea());
                }
                carTrafficRecordMapper.add(record);

                //更新用车状态
                AttendanceDeviceInfo device = attendanceDeviceInfoService.getOneByNumber(serialno);
                if(ObjectUtils.isNotEmpty(useCarManagementService.getOneByNumberAndUseStatus(license,"使用中")) && Objects.equals(device.getAccess(), "进")) {
                    //若进入营区车辆为用车申请中车辆，则修改使用状态为已返回
                    UseCarManagement useCar = useCarManagementService.getOneByNumberAndUseStatus(license,"使用中");
                    useCar.setUseStatus("已返回");
                    useCarManagementService.update(useCar,useCar.getId());
                } else if(ObjectUtils.isNotEmpty(useCarManagementService.getOneByNumberAndUseStatus(license,"未出发")) && Objects.equals(device.getAccess(), "出")) {
                    //若离开营区车辆为用车申请中车辆，则修改使用状态为用车中
                    UseCarManagement useCar = useCarManagementService.getOneByNumberAndUseStatus(license,"未出发");
                    useCar.setUseStatus("用车中");
                    useCarManagementService.update(useCar,useCar.getId());
                }
                if(ObjectUtils.isNotEmpty(carManagementService.getOneByNumber(license)) && Objects.equals(device.getAccess(), "出")) {
                    //车辆离开营区，修改车辆状态为营区外
                    CarManagement car = carManagementService.getOneByNumber(license);
                    car.setCarStatus("营区外");
                    carManagementService.update(car,car.getCarId());
                } else if(ObjectUtils.isNotEmpty(carManagementService.getOneByNumber(license)) && Objects.equals(device.getAccess(), "进")) {
                    //车辆进入营区，修改车辆状态为营区内
                    CarManagement car = carManagementService.getOneByNumber(license);
                    car.setCarStatus("营区内");
                    carManagementService.update(car,car.getCarId());
                }
            } while (false);
        } catch (JsonIOException e) {
            e.printStackTrace();
        } catch (JsonSyntaxException e) {
            e.printStackTrace();
        } catch (Exception e) {

        }
    }

    public static void onRecv(byte[] data, int len) throws UnsupportedEncodingException {
        //String ivs = new String(data,"UTF-8" );
        System.out.println("recved:" + len);
        if (len > 20 * 1024) {
            //带图片数据
            onIVSResultRecv(data, len);
        } else {
            //普通的指令响应
        }
    }

    public static boolean sendCmd(Socket socket, String cmd) {
        try {
            int len = cmd.getBytes().length;
            byte[] header = {'V', 'Z', 0, 0, 0, 0, 0, 0};
            header[4] += (byte) ((len >> 24) & 0xFF);
            header[5] += (byte) ((len >> 16) & 0xFF);
            header[6] += (byte) ((len >> 8) & 0xFF);
            header[7] += (byte) (len & 0xFF);

            OutputStream out = socket.getOutputStream();
            out.write(header);
            out.write(cmd.getBytes());
        } catch (Exception e) {
            System.out.println("Error:" + e.getMessage());
            return false;
        }
        return true;
    }

    public static boolean sendKeepAlive(Socket socket) {
        try {
            byte[] buff = {'V', 'Z', 1, 0, 0, 0, 0, 0};

            OutputStream out = socket.getOutputStream();
            out.write(buff);
        } catch (Exception e) {
            System.out.println("Error:" + e);
            return false;
        }
        return true;
    }

    public static boolean configFormat(Socket socket, int enable, int fmt, int image) {
        String cmd;
        cmd = String.format("{" +
                        "\"cmd\" : \"ivsresult\"," +
                        "\"enable\" : %s," +
                        "\"format\" : \"%s\"," +
                        "\"image\" : %s" +
                        "}",
                enable != 0 ? "true" : "false",
                fmt != 0 ? "json" : "bin",
                image != 0 ? "true" : "false");

        return sendCmd(socket, cmd);
    }

    public static int convBytesToInt(byte[] buff, int offset) {
        //4bytes 转为int，要考虑机器的大小端问题
        int len, byteValue;
        len = 0;
        byteValue = (0x000000FF & ((int) buff[offset]));
        len += byteValue << 24;
        byteValue = (0x000000FF & ((int) buff[offset + 1]));
        len += byteValue << 16;
        byteValue = (0x000000FF & ((int) buff[offset + 2]));
        len += byteValue << 8;
        byteValue = (0x000000FF & ((int) buff[offset + 3]));
        len += byteValue;
        return len;
    }

    public static int recvPacketSize(Socket socket) {
        byte[] header = new byte[8];
        int recvLen = recvBlock(socket, header, 8);
        if (recvLen <= 0) {
            return -1;
        }

        if (header[0] != 'V' || header[1] != 'Z') {
            //格式不对
            return -1;
        }

        if (header[2] == 1) {
            //心跳包
            return 0;
        }
        return convBytesToInt(header, 4);
    }

    //接收指定长度的数据，收完为止
    public static int recvBlock(Socket socket, byte[] buff, int len) {
        try {
            InputStream in = socket.getInputStream();
            int totleRecvLen = 0;
            int recvLen;
            while (totleRecvLen < len) {
                recvLen = in.read(buff, totleRecvLen, len - totleRecvLen);
                totleRecvLen += recvLen;
            }
            return len;
        } catch (Exception e) {
            System.out.println("recvBlock timeout!");
            // System.out.println("Error:"+e);
            return -1;
        }
    }

    public static int saveImage(byte[] buff, int pos, int len, String imgPath) {
        int ret = -1;
        try {
            DataOutputStream out = new DataOutputStream(new FileOutputStream(imgPath));
            out.write(buff, pos, len);
            out.close();
        } catch (IOException io) {
            System.out.println("save image failed " + imgPath);
        }

        return ret;
    }

}
